package com.wanban.downloader.utils

import android.text.TextUtils
import android.util.Log
import com.blankj.utilcode.util.CloseUtils
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.ThreadUtils
import com.wanban.downloader.MultiDownloadManager
import com.wanban.downloader.exception.HttpConnectionException
import com.wanban.downloader.listener.OnBaseDownloadListener
import com.wanban.downloader.listener.OnVideoDownloadListener
import com.wanban.downloader.listener.OnDownloadTsListener
import com.wanban.downloader.model.M3U8
import com.wanban.downloader.model.M3U8Seg
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.zip.GZIPInputStream

class TsDownloadUtils(private val listener: OnVideoDownloadListener) : OnDownloadTsListener, OnBaseDownloadListener {

    private val TAG = "Ts下载"
    // 当前已下载ts个数
    private var currentTs = 0
    // 回调时间间隔
    private var interval = 1000
    // 当前视频ts总个数
    private var totalTsCount = 0
    // 每次数据更新后的时间
    private var callbackTime = 0L
    // callback时间范围内，下载的数据内容
    private var updateProgressLength = 0L
    // 整个文件的大小
    private var currentFileLength = 0L
    // 当前要下载的m3u8
    private var m3u8: M3U8? = null
    // 线程池，固定大小 #{DOWNLOAD_TS_THREAD_COUNT}
    private var ex: ExecutorService? = null
    // 下载错误的ts集合
    private val errorTs by lazy { ArrayList<M3U8Seg>() }
    private val runningTask by lazy { ArrayList<ThreadUtils.Task<File>>() }
    @Volatile
    private var isinterrupted = false
    // 关联任务
    private lateinit var parentUrl: String

    fun downloadTsList(url: String, m3u8: M3U8, header: Map<String, String>?) {
        parentUrl = url
        // 先创建下载目录文件
        val fileDirPath = FileDownloadUtils.getOrCreateTsFileDirPath(m3u8.videoName)
        if (TextUtils.isEmpty(fileDirPath)) {
            listener.onError(url, "创建tsFileDirPath失败")
            return
        }
        Log.d(TAG, "准备下载ts列表...")
        this.m3u8 = m3u8
        errorTs.clear()
        // 回调准备开始下载
        listener.onStart(url)
        // 创建线程池
        ex = Executors.newFixedThreadPool(MultiDownloadManager.THREAD_COUNT)
        // 当前下载的ts的总个数
        totalTsCount = m3u8.tsList.size
        for (ts in m3u8.tsList) {
            if (isinterrupted) break
            // 线程池已经关闭，不在继续执行任务
            if (ex?.isShutdown == true) break
            val isExecuteSuccess = downloadTsTask(ts, fileDirPath!!, header)
            // 执行失败，就不在继续执行，一般是文件创建失败导致
            if (!isExecuteSuccess) {
                break
            }
        }
    }

    @Synchronized
    private fun downloadTsTask(ts: M3U8Seg, fileDirPath: String, header: Map<String, String>? = null): Boolean {
        // InitSegmentTask 是否正确启动
        var isInitSegmentTask = true
        if (ts.hasInitSegment) {
            Log.e(TAG, "hasInitSegment == true, 下载对应的Seg碎片...")
            val tsSegFilePath = FileDownloadUtils.getTsFilePath(fileDirPath, ts.initSegmentName)
            isInitSegmentTask = executeTsTask(ts, tsSegFilePath, header)
        }
        // InitSegmentTask 下载出错
        if (!isInitSegmentTask) {
            return false
        }
        val tsFilePath = FileDownloadUtils.getTsFilePath(fileDirPath, ts.indexName)
        return executeTsTask(ts, tsFilePath, header)
    }

    private fun executeTsTask(ts: M3U8Seg, filePath: String, header: Map<String, String>? = null): Boolean {
        return if (FileUtils.isFileExists(filePath) && FileUtils.getLength(filePath) == ts.tsSize) {
            Log.d(TAG, "${ts.indexName} => 文件已经下载...")
            ts.retryCount = 0
            ts.name = if (ts.hasInitSegment) ts.indexName else ts.initSegmentName
            if (currentTs >= totalTsCount - errorTs.size - 1) {
                onSuccess(ts, File(filePath))
            } else {
                currentTs += 1
                onProgressUpdate(ts.tsSize.toInt())
            }
            true
        } else {
            downloadFile(ts, File(filePath), header)
            true
        }
    }


    override fun cancelAllTask() {
        val leftTask = runningTask.filter { !it.isDone && !it.isCanceled }
        ThreadUtils.cancel(leftTask)
    }

    override fun delete() {

    }

    private fun downloadFile(ts: M3U8Seg, file: File, header: Map<String, String>? = null) {
        if (isinterrupted) return
        val task = getTask(ts, file, this, header)
        if (isinterrupted) return
        runningTask.add(task)
        ex?.execute(task)
    }

    private fun getTask(ts: M3U8Seg, file: File, listener: OnDownloadTsListener?, header: Map<String, String>? = null): ThreadUtils.Task<File> {
        val task = object : ThreadUtils.Task<File>() {
            var connectUtils: HttpConnectUtils? = null
            override fun doInBackground(): File {
                Log.d(TAG, "${ts.indexName} => 准备下载...")
                if (isinterrupted) return file
                // 建立http链接
                connectUtils = HttpConnectUtils()
                val url = if (ts.hasInitSegment) {
                    ts.initSegmentUri
                } else {
                    ts.url
                }
                val connection = connectUtils?.makeConnection(url, header) ?: throw NullPointerException(ErrorMessage.ERROR_INPUTSTREAM_IS_NULL)
                val responseCode = connection.responseCode
                if (responseCode < 200 || responseCode > 299) {
                    connectUtils?.close()
                    throw HttpConnectionException(ErrorMessage.ERROR_HTTPCONNECTION_ERROR)
                }
                // 判断是否要解压InputStream, 防止内容是乱码
                val isCompressed = connectUtils?.isCompressed(connection)
                var inputStream = connection.inputStream
                if (isCompressed == true) {
                    inputStream = GZIPInputStream(inputStream)
                }
                // 设置ts的大小
                ts.contentLength = connection.contentLength.toLong()
//                if (BuildConfig.DEBUG) {
//                    if (ts.index == 100) {
//                        Log.e("TEST", "强制 index = 100 的ts下载超时...")
//                        throw TimeoutException("测试下载超时...")
//                    }
//                }

                // 下载文件
                val isFileExist = FileUtils.createFileByDeleteOldFile(file)
                if (isFileExist) {
                    val isDownloadSuccess = writeFileFromIS(ts, file, inputStream, listener)
                    if (isDownloadSuccess) {
                        return file
                    } else {
                        throw NullPointerException(ErrorMessage.ERROR_DOWNLOAD_ERROR)
                    }
                } else {
                    throw NullPointerException(ErrorMessage.ERROR_FILE_CREATE_FAIL)
                }
            }

            override fun onCancel() {
                connectUtils?.close()
                // 删除创建的文件
                FileUtils.delete(file)
            }

            override fun onFail(t: Throwable?) {
                connectUtils?.close()
                // 删除创建的文件
                FileUtils.delete(file)
                if (isinterrupted) return
                Log.e("TEST", "下载出错..." + t?.message)
                listener?.onFail(ts, file, t)
            }

            override fun onSuccess(result: File) {
                connectUtils?.close()
                if (isinterrupted) return
                listener?.onSuccess(ts, file)
            }
        }
//        task.setTimeout(Constants.TIME_OUT) {
//            Log.e("TEST", "请求超时...")
//            task.connectUtils?.close()
//            // 删除创建的文件
//            FileUtils.delete(file)
//            listener?.onFail(ts, file, TimeoutException(ErrorMessage.ERROR_DOWNLOAD_FILE_TIME_OUT))
//        }
        return task
    }

    fun writeFileFromIS(ts: M3U8Seg, file: File, inputStream: InputStream?, listener: OnDownloadTsListener?): Boolean {
        if (inputStream == null || !FileUtils.createOrExistsFile(file)) {
            Log.e("FileIOUtils", "create tsFile failed.")
            return false
        }
        var os: OutputStream? = null
        val bufferSize = 524288
        return try {
            os = BufferedOutputStream(FileOutputStream(file, true), bufferSize)
            var curSize = 0L
            listener?.onProgressUpdate(0)
            val data = ByteArray(bufferSize)
            var len: Int
            while (inputStream.read(data).also { len = it } != -1) {
                os.write(data, 0, len)
                curSize += len
                if (isinterrupted) break
                // 回调下载内容
                listener?.onProgressUpdate(len)
            }
            // 修正文件大小
            if (curSize > 0 && ts.contentLength > curSize) {
                ts.contentLength = curSize
            }
            ts.tsSize = curSize
            true
        } catch (e: IOException) {
            e.printStackTrace()
            false
        } finally {
            CloseUtils.closeIO(inputStream, os)
        }
    }


    @Synchronized
    override fun onSuccess(ts: M3U8Seg, file: File) {
        currentTs += 1
        if (ts.retryCount > 0) {
            ts.retryCount = 0
            // 出错的ts，如果下载成功，要剔除错误列表
            val index = errorTs.indexOfFirst { it.url == ts.url }
            if (index >= 0) {
                errorTs.removeAt(index)
            }
        }
//        Log.e(TAG, "${ts.name} 下载成功, ts => $currentTs - $totalTsCount")
        ts.name = if (ts.hasInitSegment) ts.indexName else ts.initSegmentName
        // 未下载完全部ts文件，继续下载
        if (currentTs < totalTsCount - errorTs.size) return

        Log.d(TAG, "所有ts下载完成...检测出错的ts片段...")
        // 所有任务执行完成，最后有出错的
        // 查看是否有可以进行重试下载的ts
        val retryList = errorTs.filter { it.retryCount < Constants.MAX_RETRY_TIME }
        if (retryList.isEmpty()) { // 所有ts都尝试最大次数
            if (errorTs.size >= Constants.MAX_ERROR_TS_COUNT) {
                isinterrupted = true
                cancelAllTask()
                listener.onError(parentUrl, "出错的太多了")
            } else {
                listener.onSuccess(parentUrl)
            }
            return
        }
        retryList.forEach {
            it.retryCount += 1
            Log.e(TAG, "${it.index} => onSuccess => 准备重新开始尝试第${it.retryCount}次下载...")
            downloadFile(ts, file)
        }
    }


    override fun onCancel() {
//        listener.onError(m3u8?.url?:"", ErrorMessage.ERROR_USER_CANCEL)
    }


    override fun onFail(ts: M3U8Seg, file: File, e: Throwable?) {
        if (errorTs.any { it.url == ts.url }) { // 已存在
            Log.e(TAG, "onFail -> 已经在错误列表中...")
            if (currentTs < totalTsCount - errorTs.size) { // 没有下载完成，不予处理，都到onSuccess中处理逻辑
                Log.e(TAG, "onFail -> 没有下载完成，不予处理...")
                return
            }
            // 所有任务执行完成，最后有出错的
            // 查看是否有可以进行重试下载的ts
            val retryList = errorTs.filter { it.retryCount < Constants.MAX_RETRY_TIME }
            if (retryList.isEmpty()) { // 所有ts都尝试最大次数
                Log.e(TAG, "onFail -> 所有ts都尝试最大次数...")
                if (errorTs.size >= Constants.MAX_ERROR_TS_COUNT) {
                    isinterrupted = true
                    Log.e(TAG, "onFail -> 出错的太多了...")
                    cancelAllTask()
                    listener.onError(parentUrl, "出错的太多了")
                } else {
                    Log.e(TAG, "onFail -> 忽略出错的，当成功...")
                    listener.onSuccess(parentUrl)
                }
                return
            }
            retryList.forEach {
                it.retryCount += 1
                Log.e(TAG, "${it.index} => 准备重新开始尝试第${it.retryCount}次下载...")
                downloadFile(ts, file)
            }
        } else { // 不存在，加入错误列表
            Log.e(TAG, "onFail -> 不存在，加入错误列表...")
            if (errorTs.size >= Constants.MAX_ERROR_TS_COUNT) {
                Log.e(TAG, "onFail -> 出错的太多了...")
                isinterrupted = true
                cancelAllTask()
                listener.onError(parentUrl, "出错的太多了")
                return
            }
            errorTs.add(ts)
        }
    }

    @Synchronized
    override fun onProgressUpdate(currentLength: Int) {
        if (isinterrupted) return
        currentFileLength += currentLength
        updateProgressLength += currentLength
        if (System.currentTimeMillis() - callbackTime >= interval) {
            val percent = currentTs * 100f / totalTsCount
            if (isinterrupted) return
            listener.onProgress(parentUrl, updateProgressLength, percent, currentFileLength)
            callbackTime = System.currentTimeMillis()
            updateProgressLength = 0
        }
    }



}